#include <QtCore/QString>
#include <QtTest/QtTest>
#include <QDebug>
#include <stdlib.h>
#include "DatabaseManager.h"
#include "DatabaseManagerException.h"
#include "Group.h"
#include "Contact.h"
#include "SymmetricKeyInfo.h"
#include "KeyGenerator.h"

#include <QDebug>

using namespace kryptos::database;
using namespace kryptos::entities;
using namespace kryptos::cryptography;

class DatabaseManagerTest : public QObject
{
    Q_OBJECT

public:
    DatabaseManagerTest(){}

private Q_SLOTS:

    void storeKey();
    void addGroup();
    void addContact();
    void getGroup();
    void getAllGroups();
    void getSymmetricKeysForKeyId();
    void allSymmetricKeys();

    void initTestCase();
    void cleanupTestCase();

private:

    std::string      name, email, keyid, passphrase;
    KeyInfo          keyinfo, keyinfo2, keyinfo3, keyinfo4;
    PrivateKey       privateKey, privateKey2, privateKey3, privateKey4;
    SymmetricKeyInfo symkey, symkey2, symkey3, symkey4, symkey5;
    Contact          contact1, contact2, contact3, contact4;
    Group            group1, group2;
};

void DatabaseManagerTest::initTestCase()
{
    QFile::remove("kryptos_groups.k");
    QFile::remove("kryptos_keyring.k");

    KeyGenerator::init();

    DatabaseManager::manager().openGroups();
    DatabaseManager::manager().openKeyring();

    name = "sebastian";
    email = "seba@mail.com";
    passphrase = "sebastian";
    keyid = "0000000001";

    keyinfo = KeyInfo(KeyInfo::RSA, name, email, "test", passphrase, 1024);

    privateKey = KeyGenerator::generateEncodedKey(keyinfo, KeyInfo::RSA);
    keyinfo.setKeyid(KeyGenerator::generateKeyId(privateKey));
    keyinfo.setPassphrase(QCryptographicHash::hash(QByteArray(keyinfo.passphrase().c_str()), QCryptographicHash::Sha1).data());
    keyinfo.setTimestamp(QDate::currentDate().toString().toStdString());


    keyinfo2 = KeyInfo(KeyInfo::RSA, "contact2", "contact2@mail.com", "test", "contact2", 1024);
    privateKey2 = KeyGenerator::generateEncodedKey(keyinfo2, KeyInfo::RSA);
    keyinfo2.setKeyid(KeyGenerator::generateKeyId(privateKey2));
    keyinfo2.setPassphrase(QCryptographicHash::hash(QByteArray(keyinfo2.passphrase().c_str()), QCryptographicHash::Sha1).data());
    keyinfo2.setTimestamp(QDate::currentDate().toString().toStdString());

    keyinfo3 = KeyInfo(KeyInfo::RSA, "contact3", "contact3@mail.com", "test", "contact3", 1024);
    privateKey3 = KeyGenerator::generateEncodedKey(keyinfo3, KeyInfo::RSA);
    keyinfo3.setKeyid(KeyGenerator::generateKeyId(privateKey3));
    keyinfo3.setPassphrase(QCryptographicHash::hash(QByteArray(keyinfo3.passphrase().c_str()), QCryptographicHash::Sha1).data());
    keyinfo3.setTimestamp(QDate::currentDate().toString().toStdString());

    keyinfo4 = KeyInfo(KeyInfo::RSA, "contact4", "contact4@mail.com", "test", "contact4", 1024);
    privateKey4 = KeyGenerator::generateEncodedKey(keyinfo4, KeyInfo::RSA);
    keyinfo4.setKeyid(KeyGenerator::generateKeyId(privateKey4));
    keyinfo4.setPassphrase(QCryptographicHash::hash(QByteArray(keyinfo4.passphrase().c_str()), QCryptographicHash::Sha1).data());
    keyinfo4.setTimestamp(QDate::currentDate().toString().toStdString());

    symkey  = SymmetricKeyInfo("00000000000000000021", "testingkey.k", false, 0);
    symkey2 = SymmetricKeyInfo("00000000000000000022", "testingkey2.k", false, 100);
    symkey3 = SymmetricKeyInfo("00000000000000000023", "testingkey3.k", true, 0);
    symkey4 = SymmetricKeyInfo("00000000000000000024", "testingkey4.k", false, 0);
    symkey5 = SymmetricKeyInfo("00000000000000000025", "testingkey5.k", false, 0);

    SymmetricKeyInfoList list;

    list.push_back(symkey);
    list.push_back(symkey2);
    contact1 = Contact(keyinfo, list);

    list.clear();
    list.push_back(symkey3);
    contact2 = Contact(keyinfo2, list);

    list.clear();
    list.push_back(symkey4);
    contact3 = Contact(keyinfo3, list);

    list.clear();
    list.push_back(symkey5);
    contact4 = Contact(keyinfo4, list);

    group1 = Group("group1", Group::VERNAM);
    group1.addMember(contact1);

    group2 = Group("group2", Group::VERNAM);
    group2.addMember(contact2);
    group2.addMember(contact3);
}

void DatabaseManagerTest::storeKey()
{
    try
    {
        DatabaseManager::manager().addKeyPair(keyinfo, privateKey.key(), privateKey.publicKeyPart().key());
        DatabaseManager::manager().addKeyPair(keyinfo2, privateKey2.key(), privateKey2.publicKeyPart().key());
        DatabaseManager::manager().addKeyPair(keyinfo3, privateKey3.key(), privateKey3.publicKeyPart().key());
        DatabaseManager::manager().addKeyPair(keyinfo4, privateKey4.key(), privateKey4.publicKeyPart().key());
    }
    catch (DatabaseManagerException& e)
    {
        QFAIL("Error adding key pair");
    }
}

void DatabaseManagerTest::addGroup()
{
    try
    {
        DatabaseManager::manager().addGroup(group1);
        DatabaseManager::manager().addGroup(group2);
    }
    catch (DatabaseManagerException& e)
    {
        QFAIL("Error adding group");
    }
}

void DatabaseManagerTest::addContact()
{
    try
    {
        DatabaseManager::manager().addContactToGroup(contact4, group1);
    }
    catch (DatabaseManagerException& e)
    {
        QFAIL("Error adding group");
    }
}

void DatabaseManagerTest::getGroup()
{
    try
    {
        Group group = DatabaseManager::manager().findGroup("group1");
        QVERIFY2(group.members().size() == 2, "Should have retrieved 2 member");

        Contact member1 = group.members().front();
        Contact member2 = group.members().at(1);

        // the order in wich they are retrieve may not be always the same
        if (member1.name() != contact1.name())
        {
            Contact aux = member1;
            member1 = member2;
            member2 = aux;
        }

        QVERIFY2(member1.symmetricKeysInfo().size() == 2, "Should have retrieved 2 symmetric keys");
        QCOMPARE(member1.name(), contact1.name());
        QCOMPARE(member1.email(), contact1.email());

        QVERIFY2(member2.symmetricKeysInfo().size() == 1, "Should have retrieved 1 symmetric keys");
        QCOMPARE(member2.name(), contact4.name());
        QCOMPARE(member2.email(), contact4.email());

    }
    catch (DatabaseManagerException& e)
    {
        QString msg = "Error searching for all groups: " + QString(e.what());
        QFAIL(msg.toStdString().c_str());
    }
}

void DatabaseManagerTest::getAllGroups()
{
    try
    {
        GroupList list = DatabaseManager::manager().allGroups();
        QVERIFY2(list.size() == 2, "Should have retrieved 2 groups");
    }
    catch (DatabaseManagerException& e)
    {
        QString msg = "Error searching for all groups: " + QString(e.what());
        QFAIL(msg.toStdString().c_str());
    }
}

void DatabaseManagerTest::getSymmetricKeysForKeyId()
{
    try
    {
        SymmetricKeyInfoList list =  DatabaseManager::manager().getSymmetricKeysAssignedToKeyID(keyinfo.keyid());

        QVERIFY2(list.size()==2, "Error searching for symmetric keys");

        QCOMPARE(symkey.id()            , list[0].id());
        QCOMPARE(symkey.keyfile()       , list[0].keyfile());
        QCOMPARE(symkey.isDecryptOnly() , list[0].isDecryptOnly());
        QCOMPARE(symkey.offset()        , list[0].offset());

        QCOMPARE(symkey2.id()           , list[1].id());
        QCOMPARE(symkey2.keyfile()      , list[1].keyfile());
        QCOMPARE(symkey2.isDecryptOnly(), list[1].isDecryptOnly());
        QCOMPARE(symkey2.offset()       , list[1].offset());

        // should not bring anything
        list =  DatabaseManager::manager().getSymmetricKeysAssignedToKeyID("aaa");
        QVERIFY2(list.empty(), "Error searching for symmetric keys");
    }
    catch (DatabaseManagerException& e)
    {
        QFAIL("Error searching for symmetric keys by keyid");
    }
}

void DatabaseManagerTest::allSymmetricKeys()
{
    try
    {
        SymmetricKeyInfoList list =  DatabaseManager::manager().allSymmetricKeys();
        QVERIFY2(list.size()==5, "Error searching for symmetric keys");

        QCOMPARE(symkey.id()            , list[0].id());
        QCOMPARE(symkey.keyfile()       , list[0].keyfile());
        QCOMPARE(symkey.isDecryptOnly() , list[0].isDecryptOnly());
        QCOMPARE(symkey.offset()        , list[0].offset());

        QCOMPARE(symkey2.id()           , list[1].id());
        QCOMPARE(symkey2.keyfile()      , list[1].keyfile());
        QCOMPARE(symkey2.isDecryptOnly(), list[1].isDecryptOnly());
        QCOMPARE(symkey2.offset()       , list[1].offset());

        QCOMPARE(symkey3.id()            , list[2].id());
        QCOMPARE(symkey3.keyfile()       , list[2].keyfile());
        QCOMPARE(symkey3.isDecryptOnly() , list[2].isDecryptOnly());
        QCOMPARE(symkey3.offset()        , list[2].offset());

        QCOMPARE(symkey4.id()           , list[3].id());
        QCOMPARE(symkey4.keyfile()      , list[3].keyfile());
        QCOMPARE(symkey4.isDecryptOnly(), list[3].isDecryptOnly());
        QCOMPARE(symkey4.offset()       , list[3].offset());

        QCOMPARE(symkey5.id()           , list[4].id());
        QCOMPARE(symkey5.keyfile()      , list[4].keyfile());
        QCOMPARE(symkey5.isDecryptOnly(), list[4].isDecryptOnly());
        QCOMPARE(symkey5.offset()       , list[4].offset());

    }
    catch (DatabaseManagerException& e)
    {
        QFAIL("Error searching for all symmetric keys");
    }
}

void DatabaseManagerTest::cleanupTestCase()
{
}

QTEST_MAIN(DatabaseManagerTest);

#include "DatabaseManagerTest.moc"
